<?php


namespace GordenSong\Laravel\Utils;


use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\DateTimeType;
use GordenSong\Laravel\MySQL\RuleGetterFactory;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Str;

class TableMeta implements Arrayable
{
	/**
	 * @var array
	 */
	private $rules = [];
	/**
	 * @var Table
	 */
	private $tableSchema;
	/**
	 * @var string
	 */
	private $platform;
	/**
	 * @var string
	 */
	private $connection;
	/**
	 * @var string
	 */
	private $table;

	/**
	 * @throws \GordenSong\Laravel\Exceptions\TableNotExistException
	 */
	public function __construct(string $connection, string $table)
	{
		$this->table = $table;
		$this->connection = $connection;
		$this->tableSchema = TableUtil::load($connection, $table);;
	}

	public function getColumns(): array
	{
		return $this->tableSchema->getColumns();
	}

	/**
	 * @throws \GordenSong\Laravel\Exceptions\TableNotExistException
	 */
	public static function make(string $connection, string $table): TableMeta
	{
		return new self($connection, $table);
	}

	/**
	 * mysql, sqlite, sqlserver , etc.
	 * @return string
	 */
	public function platform(): string
	{
		return $this->platform;
	}

	/**
	 * @throws SchemaException
	 */
	public function toArray(): array
	{
		return [
			'classname' => $this->getClassName(),
			'rules' => $this->getRules(),
		];
	}

	public function getTablePrefix(): string
	{
		return DatabaseUtil::getPrefix($this->connection);
	}

	public function getTableName(): string
	{
		return substr($this->tableSchema->getName(), strlen(self::getTablePrefix()));
	}

	public function getClassName(): string
	{
		return Str::studly($this->getTableName());
	}

	/**
	 * @throws SchemaException
	 */
	public function getRules(): array
	{
		if (empty($this->rules)) {
			foreach ($this->tableSchema->getColumns() as $column) {
				$this->rules[$column->getName()] = $this->getRule($column->getName());
			}
		}
		return $this->rules;
	}

	public function getCarbonAtFields(): array
	{
		$fields = [];
		foreach ($this->tableSchema->getColumns() as $column) {
			if (in_array($column->getName(), ['created_at', 'updated_at', 'deleted_at'])
				&& $column->getType() instanceof DateTimeType) {
				$fields[] = $column->getName();
			}
		}
		return $fields;
	}

	/**
	 * @throws SchemaException
	 */
	public function getColumn($name): Column
	{
		return $this->tableSchema->getColumn($name);
	}

	/**
	 * @throws SchemaException
	 */
	public function getRule(string $name): array
	{
		$column = $this->getColumn($name);

		return $this->getColumnRules($column);
	}

	/**
	 * @param Column $column
	 * @return array
	 */
	public function getColumnRules(Column $column): array
	{
		return RuleGetterFactory::make($column)->rules($column);
	}

	public function getConnectionName(): string
	{
		return $this->connection;
	}

	public function getConnectionNamespace(): string
	{
		return Str::studly($this->getConnectionName());
	}
}
